CloudFront+S3環境上のSPA(Angular)で「/」以外のURLでリロードした場合に403(access denied)エラーとなる時の対処法
どうも!大阪オフィスの西村祐二です。
SPAをホスティングする際にCloudFront+S3環境上にデプロイして、配信することはよくあるパターンと思います。
私もAngularでSPAを開発することがよくあります。初めてCloudFront+S3環境で配信したときに、「/」以外のURLでリロードした場合に403(access denied)エラーとなる現象に遭遇して、ハマってしまいました。今回この対応策をご紹介したいと思います。
エラーを再現
ハマった現象を再現するように環境を構築していきます。対応策を早く知りたいかたは下の対応策の項目まで飛ばして大丈夫です。
CloudFront+S3の環境構築
CloudFront+S3の環境は下記ブログにCloudFormationのテンプレートが記載されているので、それを使ってサクッと構築しましょう。感謝!
AngularでSPAを実装
環境
- Angular CLI: 6.0.8
- Node: 8.11.3
- OS: darwin x64
- Angular: 6.0.7
実装
CLIを使って雛形を作成します。
$ ng new test-app --routing
/test
にルーティングしたときに表示するtest
コンポーネントを作成します。
$ ng g c test
URLが/test
のときに、test
コンポーネントが表示されるようにルーティングします。app-routing.module.ts
を編集していきます。
import { TestComponent } from './test/test.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [{ path: 'test', component: TestComponent }]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
動作確認
ローカルで動作確認します。
$ ng serve
http://localhost:4300/test
にアクセスしてみます。下記画像のように、下の方に、test works!
と表示されていたらokです。
CloudFront+S3の環境構築にデプロイ
下記コマンドで最適化した形でjs,css,htmlを吐き出してくれます。
$ ng build --prod
生成されたファイルを確認してみます。
$ ls dist/test-app 3rdpartylicenses.txt polyfills.2f4a59095805af02bd79.js favicon.ico runtime.a66f828dca56eeb90e02.js index.html styles.34c57ab7888ec1573f9c.css main.80d1aff9569d95f322a4.js
デプロイはAWS CLIでS3に先程生成されたファイルを配置することでデプロイできます。
--delete
とすることで、ローカルファイル以外のファイルがS3バケットにある場合は削除するオプションになります。
$ aws s3 sync dist/test-app s3://test-spa-assetsbucket-xxxxxxxx --delete
これで、準備はできました。cloudfrontのurlにアクセスし、/test
に移動したあとにリロードすると下記のような表示がされます。
対応策
エラーの原因として対応するファイルがないため、ブラウザリロードすると 403 エラーになってしまうようです。 そのため、CloudFrontのカスタムエラーレスポンスで、403のときに/index.html(今回は「/」)へ転送するよう設定することでこの現象を回避することができます。
設定としては、下記画像のように設定することでリロードしてもエラーとならずページが表示されます。
さいごに
いかがだったでしょうか。
はじめて、Angularで実装したSPAをS3+CloudFront環境上で配信したときにハマったことと、その対応策をご紹介しました。
誰かの参考になれば幸いです。